/*
 * Copyright 2025 arisgo@163.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.arisgo.cloud.web.configurer;

import com.arisgo.cloud.web.configurer.resolve.ArisgoWebMvcErrorAttributes;
import com.arisgo.cloud.web.mybatis.interceptor.MybatisInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.spring.FlexSqlSessionFactoryBean;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.sql.DataSource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * @author Arisgo
 * @since 2024/6/8
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(value = {MybatisProperties.class})
public class ArisgoWebMvcConfigurer implements WebMvcConfigurer {

    /**
     * 跨域配置
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.PUT.name(), HttpMethod.DELETE.name())
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600)
                .exposedHeaders(HttpHeaders.AUTHORIZATION);
    }

    /**
     * js接收long数据会精度丢失
     * 此方法将返回前端的long转为string
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();

        ObjectMapper mapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);

        simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

        simpleModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        simpleModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        mapper.registerModule(simpleModule);
        converter.setObjectMapper(mapper);

        converters.add(0, converter);
    }

    //    /**
//     * 暂时没用 和ArisgoGlobalExceptionResolver联合使用
//     * 自定义全局异常处理
//     * @param resolvers initially an empty list
//     */
//    @Override
//    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
//        // @ExceptionHandler 处理类
//        resolvers.add(new ExceptionHandlerExceptionResolver());
//        // ResponseStatusException 处理类，异常需要手动抛出
//        resolvers.add(new ResponseStatusExceptionResolver());
//        // 自定义异常处理类，继承于DefaultHandlerExceptionResolver，并取代了DefaultHandlerExceptionResolver
//        resolvers.add(new ArisgoGlobalExceptionResolver());
//    }

    /**
     * Swagger3 配置
     */
    @Bean
    public OpenAPI openApi() {
        return new OpenAPI().info(new Info()
                .version("1.0")
                .title("Arisgo Cloud Web Api Manage")
                .description("Web Mvc Api Doc")
                .contact(new Contact().name("Arisgo").email("arisgo@163.com"))
                .license(new License().name("Apache 2.0")));
    }

    /**
     * 配置 mybatis-flex
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, MybatisProperties mybatisProperties, MybatisInterceptor mybatisInterceptor) throws Exception {
        // 关闭 banner
        FlexGlobalConfig.getDefaultConfig().setPrintBanner(false);
        // 配置flex
        SqlSessionFactoryBean factoryBean = new FlexSqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setPlugins(mybatisInterceptor);
        factoryBean.setMapperLocations(mybatisProperties.resolveMapperLocations());

        FlexConfiguration configuration = new FlexConfiguration();
        configuration.setLogImpl(mybatisProperties.getConfiguration().getLogImpl());

        factoryBean.setConfiguration(configuration);
        return factoryBean.getObject();
    }

    @Bean
    public ErrorAttributes getErrorAttributes() {
        return new ArisgoWebMvcErrorAttributes();
    }

}
